// #include <H5Gpublic.h>
// #include <H5Lpublic.h>
// #include <H5Ppublic.h>
// #include <H5Tpublic.h>
#include <cmath>
#include <cstdint>
#include <string_view>
#include <vector>
#include <string_view>
#include "hdf5_util.h"

namespace h5 {
static hid_t u8_type_id    = get_dtype_id<uint8_t>();
static hid_t i8_type_id    = get_dtype_id<int8_t>();
static hid_t u16_type_id   = get_dtype_id<uint16_t>();
static hid_t i16_type_id   = get_dtype_id<int16_t>();
static hid_t u32_type_id   = get_dtype_id<uint32_t>();
static hid_t i32_type_id   = get_dtype_id<int32_t>();
static hid_t u64_type_id   = get_dtype_id<uint64_t>();
static hid_t i64_type_id   = get_dtype_id<int64_t>();
static hid_t f16_type_id   = get_dtype_id<float16>();
static hid_t f32_type_id   = get_dtype_id<float>();
static hid_t f64_type_id   = get_dtype_id<double>();
static hid_t str32_type_id = get_dtype_id<fixed_s32_t>();
static hid_t str64_type_id = get_dtype_id<fixed_s64_t>();



static const std::array<hid_t, 10> builtin_dtypes = {get_dtype_id<uint8_t>(),
                                                     get_dtype_id<int8_t>(),
                                                     get_dtype_id<uint16_t>(),
                                                     get_dtype_id<int16_t>(),
                                                     get_dtype_id<uint32_t>(),
                                                     get_dtype_id<int32_t>(),
                                                     get_dtype_id<uint64_t>(),
                                                     get_dtype_id<int64_t>(),
                                                     get_dtype_id<float>(),
                                                     get_dtype_id<double>()};

bool is_builtin_dtype_id(hid_t type_id) {
    H5I_type_t type_kind = H5Iget_type(type_id);
    if (type_kind != H5I_DATATYPE) {
        return false;
    }
    for (size_t i = 0; i < builtin_dtypes.size(); ++i) {
        if (type_id == builtin_dtypes[i] || H5Tequal(type_id, builtin_dtypes[i]) > 0) {
            return true;
        }
    }
    return false;
}

SimpleHDF5ResourceKind get_resource_kind(hid_t resource_id) {
    if (resource_id < 0) {
        return SimpleHDF5ResourceKind::other;
    }
    H5I_type_t type_kind = H5Iget_type(resource_id);
    if (type_kind == H5I_FILE) {
        return SimpleHDF5ResourceKind::file;
    } else if (type_kind == H5I_GROUP) {
        return SimpleHDF5ResourceKind::group;
    } else if (type_kind == H5I_DATASET) {
        return SimpleHDF5ResourceKind::dataset;
    } else if (type_kind == H5I_DATASPACE) {
        return SimpleHDF5ResourceKind::dataspace;
    } else if (type_kind == H5I_DATATYPE) {
        if (is_builtin_dtype_id(resource_id)) {
            return SimpleHDF5ResourceKind::builtin_dtype;
        } else {
            return SimpleHDF5ResourceKind::structured_dtype;
        }
    } else if (type_kind == H5I_ATTR) {
        return SimpleHDF5ResourceKind::attribute;
    } else if (type_kind == H5I_GENPROP_LST) {
        return SimpleHDF5ResourceKind::property;
    }
    return SimpleHDF5ResourceKind::other;
}

CompressFilterType get_dataset_filter_type(hid_t dataset_id) {
    if (dataset_id <= 0) {
        return CompressFilterType::FilterOther;
    }
    hid_t plist_id = H5Dget_create_plist(dataset_id);
    if (plist_id < 0) {
        return CompressFilterType::FilterOther;
    }
    DECLARE_HDF5_RESOURCE_MANAGER(plist_id);

    size_t       nelmts = 0;
    unsigned int flags;
    unsigned int filter_info;
    H5Z_filter_t filter_type =
        H5Pget_filter(plist_id, 0, &flags, &nelmts, nullptr, 0, nullptr, &filter_info);
    CompressFilterType ret;
    if (filter_type == H5Z_FILTER_DEFLATE) {
        ret = CompressFilterType::FilterDefalte;
    } else if (filter_type == H5Z_FILTER_SHUFFLE) {
        ret = CompressFilterType::FilterShuffle;
    } else if (filter_type == H5Z_FILTER_FLETCHER32) {
        ret = CompressFilterType::FilterFlecher32;
    } else if (filter_type == H5Z_FILTER_SZIP) {
        ret = CompressFilterType::FilterSzip;
    } else if (filter_type == H5Z_FILTER_NBIT) {
        ret = CompressFilterType::FilterNbit;
    } else if (filter_type == H5Z_FILTER_SCALEOFFSET) {
        ret = CompressFilterType::FilterScaleOffset;
    } else {
        ret = CompressFilterType::FilterOther;
    }
    return ret;
}

hid_t open_file_without_file_locking(const char* file) {
    // empty file path
    if (!file || !file[0]) {
        return INVALID_HDF5_RESOURCE_ID;
    }

    hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS);
    if (plist_id < 0) {
        return INVALID_HDF5_RESOURCE_ID;
    }
    DECLARE_HDF5_RESOURCE_MANAGER(plist_id);
    if (H5Pset_file_locking(plist_id, false, true) < 0) {
        return INVALID_HDF5_RESOURCE_ID;
    }
    hid_t file_id = H5Fopen(file, H5F_ACC_RDONLY, plist_id);
    return file_id;
}

hid_t open_file_without_file_locking(const std::string& file) {
    return open_file_without_file_locking(file.c_str());
}

hid_t create_file_with_auto_flush(const char* file) {
    hid_t access_plist_id = H5Pcreate(H5P_FILE_ACCESS);
    if (access_plist_id < 0) {
        return INVALID_HDF5_RESOURCE_ID;
    }
    DECLARE_HDF5_RESOURCE_MANAGER(access_plist_id);
    if (H5Pset_fclose_degree(access_plist_id, H5F_CLOSE_STRONG) < 0) {
        return INVALID_HDF5_RESOURCE_ID;
    }
    hid_t file_id = H5Fcreate(file, H5F_ACC_TRUNC, H5P_DEFAULT, access_plist_id);
    return file_id;
}

hid_t create_file_with_auto_flush(const std::string& file) {
    return create_file_with_auto_flush(file.c_str());
}

std::vector<std::string_view> split_str(std::string_view s1, char sep) {
    std::vector<std::string_view> result;
    size_t                        left = 0;
    for (size_t i = 0; i < s1.size(); ++i) {
        if (s1[i] == sep) {
            result.push_back(s1.substr(left, i - left));
            left = i + 1;
        }
    }
    // append last!
    result.push_back(s1.substr(left, s1.size() - left));
    return result;
}
bool is_link_exist(hid_t loc_id, const char* name) {
    return H5Lexists(loc_id, name, H5P_DEFAULT) > 0;
}

bool is_link_exist(hid_t loc_id, const std::string& name) {
    return is_link_exist(loc_id, name.c_str());
}


std::vector<std::string> get_sub_names(hid_t loc_id) {
    std::vector<std::string> sub_names;
    if (loc_id < 0) {
        return sub_names;
    }
    SimpleHDF5ResourceKind resource_kind = get_resource_kind(loc_id);
    // must be a file or group
    if (resource_kind != SimpleHDF5ResourceKind::file &&
        resource_kind != SimpleHDF5ResourceKind::group) {
        return sub_names;
    }

    hsize_t n = 0;
    H5Gget_num_objs(loc_id, &n);
    if (n <= 0) {
        return sub_names;
    }
    sub_names.reserve(n);
    for (hsize_t i = 0; i < n; ++i) {
        auto        len = H5Gget_objname_by_idx(loc_id, i, nullptr, 0);
        std::string sub_name(len, 0);
        H5Gget_objname_by_idx(loc_id, i, &sub_name[0], sub_name.size() + 1);
        sub_names.push_back(std::move(sub_name));
    }
    return sub_names;
}
std::string get_first_sub_name(hid_t loc_id) {
    std::string name;
    if (loc_id < 0) {
        return name;
    }
    SimpleHDF5ResourceKind resource_kind = get_resource_kind(loc_id);
    // must be a file or group
    if (resource_kind != SimpleHDF5ResourceKind::file &&
        resource_kind != SimpleHDF5ResourceKind::group) {
        return name;
    }
    hsize_t n = 0;
    H5Gget_num_objs(loc_id, &n);
    if (n <= 0) {
        return name;
    }
    auto len = H5Gget_objname_by_idx(loc_id, 0, nullptr, 0);
    name.resize(n);
    H5Gget_objname_by_idx(loc_id, 0, &name[0], name.size() + 1);
    return name;
}
}   // namespace h5
